﻿using UnityEngine;
using System.Collections;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System;
using ProtoBuf.Meta;
using ProtoBuf;
using System.IO;
using PBMessage;
public class NetWork
{
    Thread m_hConnectThread; 
    PacketHandlersDic HandlerDic;
    PBIDMapping pbMapping; 

    SocketInstance m_Socket;
    ConnectStatus m_ConnectStatus;
    string m_ServerAddr;
    int m_ServerPort;
    int m_SleepTime;
    float m_timeConnectBegin;
    private string m_strConnectResult = "";
    private bool m_bConnectFinish = false;
    //每帧处理最多包数
    private int m_nEachFrame_ProcessPacket_Count;
    //收发消息包状态
    private bool m_bCanProcessPacket = true;
    public bool CanProcessPacket
    {
        get { return m_bCanProcessPacket; }
        set { m_bCanProcessPacket = value; }
    }

    SocketInputStream m_SocketInputStream;
    SocketOutputStream m_SocketOutputStream;



    //收发包流量统计
    public static int s_nReceiveCount = 0;
    public static int s_nSendCount = 0;

    public enum ConnectStatus
    {
        INVALID,
        CONNECTING,    //连接中
        CONNECTED,     //已连接
        DISCONNECTED   //已断连
    }
    public const uint SOCKET_ERROR = 0xFFFFFFFF;
    public const int PACKET_HEADER_SIZE = 6;
    int EACHFRAME_PROCESSPACKET_COUNT = 12;
    //包头缓存
    private byte[] m_HeadbyteData;

    public delegate void ConnectDelegate(bool bSuccess, string result);
    private  ConnectDelegate m_delConnect = null;
    public void Init() 
    {
        HandlerDic = new PacketHandlersDic();
        HandlerDic.LoadHandlers();
        m_Socket = new SocketInstance();
        m_HeadbyteData=  new byte[PACKET_HEADER_SIZE];
        m_nEachFrame_ProcessPacket_Count = EACHFRAME_PROCESSPACKET_COUNT;
    }


    public void ConnectToServer(string ServerAddr, int ServerPort,int sleepTime)
    {
        if (m_ConnectStatus == ConnectStatus.CONNECTING)
        {
            return;
        }
        m_ServerAddr = ServerAddr;
        m_ServerPort = ServerPort;
        m_SleepTime = sleepTime;
        m_hConnectThread = new Thread(new ParameterizedThreadStart(_ConnectThread));
        m_timeConnectBegin = Time.time;
        m_hConnectThread.Start(this);
    }

    public void ConnectThread()
    {
        m_ConnectStatus = ConnectStatus.CONNECTING;
        while (true)
        {
            Debug.Log("connect:" + m_ServerAddr+":"+m_ServerPort);
            m_strConnectResult = m_Socket.connect(m_ServerAddr,m_ServerPort);
            if (m_strConnectResult.Length == 0 && m_Socket.IsValid)
            {
                m_SocketInputStream = new SocketInputStream(m_Socket);
                m_SocketOutputStream = new SocketOutputStream(m_Socket);
                m_ConnectStatus = ConnectStatus.CONNECTED;
                break;
            }
            else
            {
                Debug.LogError(m_strConnectResult);
            }
            m_Socket.close();

            Thread.Sleep(m_SleepTime);
            m_ConnectStatus = ConnectStatus.DISCONNECTED;
            break;
        }
        m_bConnectFinish = true;
    }


    protected static void _ConnectThread(object me)
    {
        ((NetWork)me).ConnectThread();
    }

    public void Update()
    {
        if (m_bConnectFinish) 
        {
            m_bConnectFinish = false;
            if (null != m_delConnect)
            {
                m_delConnect(m_ConnectStatus == ConnectStatus.CONNECTED, m_strConnectResult);
            }
        }
        WaitPacket();
    }

    void WaitPacket() 
    {
     
            if (!m_Socket.IsValid)
            {
                return;
            }
            if (!m_Socket.IsConnected)
            {
                return;
            }
        
            if (!ProcessInput())
            {
                return;
            }
            if (!ProcessOutput())
            {
                return;
            }
            ProcessPacket();
      
    }


    bool ProcessInput()
    {
        if (m_SocketInputStream == null)
        {
            return false;
        }
        if (m_Socket.IsCanReceive() == false)
        {
            return true;
        }

        uint nSizeBefore = m_SocketInputStream.Length();
        uint ret = m_SocketInputStream.Fill();
        uint nSizeAfter = m_SocketInputStream.Length();
        if (ret == SOCKET_ERROR)
        {
            Debug.LogWarning("send packet fail");
            m_Socket.close();
            ConnectLost();
            return false;
        }
        //收包统计
        if (nSizeAfter > nSizeBefore)
        {
            if (s_nReceiveCount < 0)
            {
                s_nReceiveCount = 0;
            }
             s_nReceiveCount += (int)(nSizeAfter - nSizeBefore);
        }
        return true;
    }

    bool ProcessOutput()
    {
        if (m_SocketOutputStream == null)
        {
            return false;
        }
        if (m_Socket.IsCanSend() == false)
        {
            return true;
        }

        uint ret = m_SocketOutputStream.Flush();
        if (ret == SOCKET_ERROR)
        {
            Debug.LogWarning("send packet fail");
            m_Socket.close();
            ConnectLost();
            return false;
        }
        return true;
    }

    void ConnectLost()
    {
        m_ConnectStatus = ConnectStatus.DISCONNECTED;
    }

    public  void SetConcnectDelegate(ConnectDelegate delConnectFun)
    {
        m_delConnect = delConnectFun;
    }
    //收包缓存
    private int m_MaxRevOnePacketbyteCount;
    private byte[] m_MaxRevOnePacketbyte;
    void ProcessPacket()
    {
        if (m_SocketInputStream == null)
        {
            return;
        }
        Int32 packetSize;
        Int16 messageid;
        int nProcessPacketCount = m_nEachFrame_ProcessPacket_Count;
        while (m_bCanProcessPacket && (nProcessPacketCount--) > 0)
        {
            Array.Clear(m_HeadbyteData, 0, PACKET_HEADER_SIZE);
            if (!m_SocketInputStream.Peek(m_HeadbyteData, PACKET_HEADER_SIZE))
            {
                break;
            }
           
            //包大小
            packetSize = BitConverter.ToInt32(m_HeadbyteData, 0);
            packetSize = System.Net.IPAddress.NetworkToHostOrder(packetSize) ;
            //消息ID
            messageid = BitConverter.ToInt16(m_HeadbyteData, sizeof(UInt32));
            messageid = System.Net.IPAddress.NetworkToHostOrder(messageid);
            if (m_SocketInputStream.Length() < packetSize)
            {
                Debug.LogError("包头 与实际大小不一致");
                break;
            }
           
                //初始化一块和收到的包一样大小的缓存.
                if (m_MaxRevOnePacketbyteCount < packetSize)
                {
                    m_MaxRevOnePacketbyte = new byte[packetSize];
                    m_MaxRevOnePacketbyteCount = packetSize;
                }
                Array.Clear(m_MaxRevOnePacketbyte, 0, m_MaxRevOnePacketbyteCount);
                //跳过包头.
                bool bRet = m_SocketInputStream.Skip(PACKET_HEADER_SIZE);
                if (bRet == false)
                {
                    string errorLog = string.Format("Can not Create Packet MessageID({0},packetSize{1})", messageid, packetSize);
                    Debug.LogError(errorLog);
                }
                //读数据.
                m_SocketInputStream.Read(m_MaxRevOnePacketbyte, (uint)(packetSize));
                //获取Handler.
                 pbMapping = HandlerDic.GetPBHandler((PBMessageID)messageid);
                 if (pbMapping == null)
                 {
                     Debug.LogError("找不到消息包 messageID:"+messageid.ToString());
                 }
                 using (MemoryStream ms = new MemoryStream(m_MaxRevOnePacketbyte,0,packetSize))
                 {
                     object packetData = RuntimeTypeModel.Default.Deserialize(ms, null, pbMapping.HandlerType);
                     if (packetData == null)
                     {
                         Debug.LogError("数据反序列化失败");
                     }
                     pbMapping.HandlerClass.Exec(packetData);
                 }
            }
           
        //当每帧的包不够时自动+4
        if (nProcessPacketCount >= 0)
        {
            m_nEachFrame_ProcessPacket_Count = EACHFRAME_PROCESSPACKET_COUNT;
        }
        else
        {
            m_nEachFrame_ProcessPacket_Count += 4;
        }
    }

    public void SendPacket<T>(T packet, PBMessageID msgId) 
    {
        if (packet == null)
        {
            return;
        }
     
        if (m_ConnectStatus != ConnectStatus.CONNECTED)
        {
            if (m_ConnectStatus == ConnectStatus.DISCONNECTED)
            {
                ConnectLost();
            }
            return;
        }

        if (m_Socket.IsValid)
        {

            
            using (MemoryStream ms = new MemoryStream())
            {
                Serializer.Serialize<T>(ms, packet);
                byte[] actual = ms.ToArray();

                int smallsize = System.Net.IPAddress.HostToNetworkOrder(actual.Length + 2);
                byte[] PacketLen = System.BitConverter.GetBytes(smallsize);
                m_SocketOutputStream.Write(PacketLen, 4);

                System.Int16 smallMsgId = (System.Int16)System.Net.IPAddress.HostToNetworkOrder((System.Int16)msgId);
                byte[] MsgIdBytes = System.BitConverter.GetBytes(smallMsgId);
                m_SocketOutputStream.Write(MsgIdBytes, 2);

                m_SocketOutputStream.Write(actual,(uint)actual.Length);
            }

      

        }
    }

}
